package cc.rengu.oltp.service.realize.impl;

import cc.rengu.jradp.application.BulletinBoard;
import cc.rengu.jradp.asynnotify.AsynNotify;
import cc.rengu.jradp.dubbo.service.BaseService;
import cc.rengu.jradp.mods.xml.XmlTree;
import cc.rengu.jradp.service.dubbo.Dubbo;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.ServiceNameConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.ChannelCallCfgMapper;
import cc.rengu.oltp.service.common.dao.SrcTxnAuthCfgMapper;
import cc.rengu.oltp.service.common.dao.SrcTxnExcpCfgMapper;
import cc.rengu.oltp.service.common.dao.impl.ChannelCallCfgMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SrcTxnAuthCfgMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SrcTxnExcpCfgMapperImpl;
import cc.rengu.oltp.service.common.entity.ChannelCallCfg;
import cc.rengu.oltp.service.common.entity.SrcTxnAuthCfg;
import cc.rengu.oltp.service.common.entity.SrcTxnExcpCfg;
import cc.rengu.oltp.service.common.enums.OltpRpcdEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizHeader;
import cc.rengu.oltp.service.model.DubboResult;
import cc.rengu.oltp.service.model.TxnInfo;
import cc.rengu.oltp.service.realize.ChannelService;
import cc.rengu.oltp.service.realize.DubboService;
import cc.rengu.oltp.service.realize.TxnInfoService;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import cc.rengu.utility.threadarea.ThreadVar;
import cc.rengu.utility.xml.dom.Node;
import com.alibaba.fastjson.JSON;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * Dubbo服务调用实现类
 * Created by 王成 on 2018/7/3.
 */
public class DubboServiceImpl extends BaseService<Object, Object> implements DubboService {
    private static final boolean doNotPretty = false;
    private RgLogger rglog = RgLog.getLogger(this.getClass().getName());
    private static final String DUBBO_INNER_CALL_FLAG = "DUBBO_INNER_CALL_FLAG";
    private static final String DUBBO_INNER_CALL_NUM = "DUBBO_INNER_CALL_NUM";

    @Override
    public DubboResult callBizService(Object request, Class rspClass) throws Exception {
        try {
            resourceInit();
            XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
            DubboResult dubboResult = new DubboResult();
            rglog.debug("Dubbo请求报文:{}", JSON.toJSONString(request, doNotPretty));
            /* 将业务请求信息转换到内部树 */
            ConvertUtil.convertInput(request);
            /* 获取交易配置信息 */
            TxnInfoService txnInfoService = new TxnInfoServiceImpl();
            TxnInfo txnInfo = txnInfoService.getCurrentTxnInfo();
            /* 将交易信息设置到交易内部树 */
            xmlTreeUtil.setXmlTreeObjectValue(TreeNodeConstant.TXN_INFO, txnInfo);
            /* 交易权限检查控制 */
            ChannelService channelService = new ChannelServiceImpl();
            if (!channelService.channnelCheck(txnInfo)) {
                rglog.error("instId:<{}>,channel:<{}>,txnNum:<{}>,bizType:<{}> is not support.",
                        txnInfo.getSrcTxnAuthCfg().getInstId(), txnInfo.getSrcTxnAuthCfg().getMsgSrcId(), txnInfo.getSrcTxnAuthCfg().getTxnNum(), txnInfo.getSrcTxnAuthCfg().getBizType());
                throw new BizException(OltpRpcdEnum.TRANS_NOT_SUPPORT);
            }
            /* 调用业务处理服务 */
            int iReturnCode = callService(txnInfo.getSrcTxnAuthCfg().getServiceName().trim(), 0);
            if (0 != iReturnCode) {
                rglog.error("调用交易<{}-{}>失败,调用服务名:<{}>,iReturnCode:<{}>", txnInfo.getTxnKey(),
                        txnInfo.getSrcTxnAuthCfg().getTxnName(), txnInfo.getSrcTxnAuthCfg().getServiceName(), iReturnCode);
                dubboResult.setSuccess(false);
                if (98 == iReturnCode) {
                    dubboResult.setRespCode(OltpRpcdEnum.BIZ_PROCESS_TIMEOUT.getRespCode());
                    dubboResult.setRespDesc(OltpRpcdEnum.BIZ_PROCESS_TIMEOUT.getRespDesc());
                } else {
                    dubboResult.setRespCode(OltpRpcdEnum.CALL_BIZ_SERVICE_ERROR.getRespCode());
                    dubboResult.setRespDesc(OltpRpcdEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
                }
            } else {
                Object response = rspClass.newInstance();
                ConvertUtil.convertOutput(response);
                dubboResult.setSuccess(true);
                dubboResult.setResult(response);
                dubboResult.setRespCode(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.RESP_CODE));
                dubboResult.setRespDesc(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.RESP_DESC));
            }
            rglog.debug("Dubbo响应报文:{}", JSON.toJSONString(dubboResult, doNotPretty));
            return dubboResult;
        } catch (Exception e) {
            rglog.error("Dubbo调用业务服务异常!");
            throw new BizException(OltpRpcdEnum.OLTP_PROCESS_ERROR.getRespCode(), OltpRpcdEnum.OLTP_PROCESS_ERROR.getRespDesc());
        } finally {
            resourceRelease();
        }
    }

    @Override
    public DubboResult callAtomService(Object request, String serviceName, Class rspClass) throws Exception {
        try {
            resourceInit();
            XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
            DubboResult dubboResult = new DubboResult();
            rglog.debug("Dubbo请求报文:{}", JSON.toJSONString(request, doNotPretty));
            /* 将业务请求信息转换到内部树 */
            ConvertUtil.convertInput(request);
            /* 调用业务处理服务 */
            int iReturnCode = callService(serviceName, 0);
            if (0 != iReturnCode) {
                rglog.error("调用服务<{}>失败,iReturnCode:<{}>", serviceName, iReturnCode);
                dubboResult.setSuccess(false);
                if (98 == iReturnCode) {
                    dubboResult.setRespCode(OltpRpcdEnum.BIZ_PROCESS_TIMEOUT.getRespCode());
                    dubboResult.setRespDesc(OltpRpcdEnum.BIZ_PROCESS_TIMEOUT.getRespDesc());
                } else {
                    dubboResult.setRespCode(OltpRpcdEnum.CALL_BIZ_SERVICE_ERROR.getRespCode());
                    dubboResult.setRespDesc(OltpRpcdEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
                }
            } else {
                Object response = rspClass.newInstance();
                ConvertUtil.convertOutput(response);
                dubboResult.setSuccess(true);
                dubboResult.setResult(response);
                dubboResult.setRespCode(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.RESP_CODE));
                dubboResult.setRespDesc(xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.RESP_DESC));
            }
            rglog.debug("Dubbo响应报文:{}", JSON.toJSONString(dubboResult, doNotPretty));
            return dubboResult;
        } catch (Exception e) {
            rglog.error("Dubbo调用原子服务异常!");
            throw new BizException(OltpRpcdEnum.OLTP_PROCESS_ERROR.getRespCode(), OltpRpcdEnum.OLTP_PROCESS_ERROR.getRespDesc());
        } finally {
            resourceRelease();
        }
    }

    @Override
    public Object callDubboService(String dubboBean, String dubboMethod, Object dubboRequest) throws Exception {
        try {
            rglog.debug("Dubbo请求报文:{}", JSON.toJSONString(dubboRequest, doNotPretty));
            Object interfaceService = Dubbo.getBean(dubboBean);
            Method[] methods = interfaceService.getClass().getDeclaredMethods();
            Optional<Method> methodOpt = Arrays.stream(methods).filter(item -> item.getName().equals(dubboMethod)).findFirst();
            if (methodOpt.isPresent()) {
                Object dubboResponse = methodOpt.get().invoke(interfaceService, dubboRequest);
                rglog.debug("dubbo调用结果：{}", JSON.toJSONString(dubboResponse, doNotPretty));
                return dubboResponse;
            } else {
                rglog.error("远程Dubbo服务<{}>无<{}>方法!", dubboBean, dubboMethod);
                return null;
            }
        } catch (Exception e) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("远程Dubbo服务调用异常:<{}>", byteArrayOutputStream.toString());
            return null;
        }
    }

    @Override
    public Object callDubboInnerMethod(Class innerClasz, String innerMethod, Object innerRequest) throws Exception {
        String innerClaszName = innerClasz.getName();
        if (!innerClaszName.endsWith("Impl") && !innerClasz.isInterface()) {
            throw new Exception("未执行调用，请使用接口类方式");
        }
        //保留原交易码现场
        //保留MessageTree,消息树在message，不涉及radp平台层的xml相关的节点的变动
        XmlTree xmlTree = new XmlTree();
        dubboInnerCallBegin();
        Node messageNode = xmlTree.getNode("message");
        xmlTree.deleteNode("message");
        try {
            rglog.debug("Dubbo应用内调用请求报文:{}", JSON.toJSONString(innerRequest, doNotPretty));
            //内部类，匿名类调用存在问题，此处都是普通类
            Class clazz;
            if (!innerClaszName.endsWith("Impl")) {
                String packageName = innerClasz.getPackage().getName();
                packageName = packageName.substring(0, packageName.length() - 6);
                try {
                    String className = packageName + "biz.impl." + innerClasz.getSimpleName() + "Impl";
                    clazz = Class.forName(className);
                } catch (Exception e) {
                    String className = packageName + "biz.service.impl." + innerClasz.getSimpleName() + "Impl";
                    clazz = Class.forName(className);
                }
            } else {
                clazz = innerClasz;
            }
            Object innerServiceInst = clazz.newInstance();
            Method[] methods = innerServiceInst.getClass().getDeclaredMethods();
            Optional<Method> methodOpt = Arrays.stream(methods).filter(item -> item.getName().equals(innerMethod)).findFirst();
            if (methodOpt.isPresent()) {
                //到了这一步所有的异常都转为未知的异常
                Object innerResponse = methodOpt.get().invoke(innerServiceInst, innerRequest);
                rglog.debug("dubbo应用内方法调用结果：{}", JSON.toJSONString(innerResponse, doNotPretty));
                if (null == innerResponse) {
                    throw new BizException(OltpRpcdEnum.BIZ_PROCESS_TIMEOUT);
                }
                return innerResponse;
            } else {
                rglog.error("<{}>中无<{}>方法!", innerClasz.getName(), innerMethod);
                throw new NoSuchMethodException();
            }
        } catch (Exception e) {
            rglog.error("Dubbo应用内服务调用异常:{}", cc.rengu.utility.base.StringUtil.ExceptionToString(e));
            throw new BizException(OltpRpcdEnum.BIZ_PROCESS_TIMEOUT);
        } finally {
            dubboInnerCallEnd();
            xmlTree.deleteNode("message");
            xmlTree.getRoot().add(messageNode);
        }
    }

    @Override
    public int asynCallDubboService(String dubboBean, String dubboMethod, Object dubboRequest, String asynServiceName) throws Exception {
        return asynCallDubboService(dubboBean, dubboMethod, dubboRequest, asynServiceName, 1, 0, 0);
    }

    @Override
    public int asynCallDubboService(String dubboBean, String dubboMethod, Object dubboRequest, String asynServiceName, int callTimes, int gatType, int gatTime) throws Exception {
        rglog.debug("Dubbo异步请求报文:{}", JSON.toJSONString(dubboRequest, doNotPretty));
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        if (null == asynServiceName || asynServiceName.isEmpty()) {
            asynServiceName = ServiceNameConstant.DUBBO_ASYN_BASE_SERVICE;
        }
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.DUBBO_BEAN_NAME, dubboBean);
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.DUBBO_METHOD_NAME, dubboMethod);
        xmlTreeUtil.setXmlTreeObjectValue(TreeNodeConstant.DUBBO_REQUEST, dubboRequest);
        AsynNotify asynNotify = new AsynNotify();
        rglog.debug("正在异步调用远程Dubbo服务，dubboBean:<{}>,dubboMethod:<{}>,asynService:<{}>", dubboBean, dubboMethod, asynServiceName);
        int iReturnCode = asynNotify.transactionNotice(asynServiceName, callTimes, gatType, gatTime);
        if (0 != iReturnCode) {
            rglog.error("异步调用Dubbo服务提交失败,iReturnCode:<{}>", iReturnCode);
        }
        rglog.debug("异步调用Dubbo服务提交成功,iReturnCode:<{}>", iReturnCode);
        return iReturnCode;
    }

    @Override
    public int asynCallDubboInnerMethod(Class innerClasz, String dubboMethod, Object dubboRequest, String asynServiceName) throws Exception {
        return asynCallDubboInnerMethod(innerClasz, dubboMethod, dubboRequest, asynServiceName, 1, 0, 0);
    }

    @Override
    public int asynCallDubboInnerMethod(Class innerClasz, String dubboMethod, Object dubboRequest, String asynServiceName, int callTimes, int gatType, int gatTime) throws Exception {
        String innerClaszName = innerClasz.getName();
        if (!innerClaszName.endsWith("Impl") && !innerClasz.isInterface()) {
            throw new Exception("未执行调用，请使用接口类方式");
        }
        rglog.debug("Dubbo异步请求报文:{}", JSON.toJSONString(dubboRequest, doNotPretty));
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        if (null == asynServiceName || asynServiceName.isEmpty()) {
            asynServiceName = ServiceNameConstant.DUBBO_ASYN_BASE_SERVICE;
        }
        //内部类，匿名类调用存在问题，此处都是普通类
        String packageName = innerClasz.getPackage().getName();
        packageName = packageName.substring(0, packageName.length() - 6);
        String className;
        if (!innerClaszName.endsWith("Impl")) {
            try {
                className = packageName + "biz.impl." + innerClasz.getSimpleName() + "Impl";
                Class.forName(className);
            } catch (Exception e) {
                className = packageName + "biz.service.impl." + innerClasz.getSimpleName() + "Impl";
                Class.forName(className);
            }
        } else {
            className = innerClasz.getName();
        }
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.DUBBO_ASYN_FLAG, "true");
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.DUBBO_CLASS_NAME, className);
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.DUBBO_METHOD_NAME, dubboMethod);
        xmlTreeUtil.setXmlTreeObjectValue(TreeNodeConstant.DUBBO_REQUEST, dubboRequest);
        AsynNotify asynNotify = new AsynNotify();
        rglog.debug("正在异步调用远程Dubbo服务，dubboClassName:<{}>,dubboMethod:<{}>,asynService:<{}>", innerClasz.getName(), dubboMethod, asynServiceName);
        int iReturnCode = asynNotify.transactionNotice(asynServiceName, callTimes, gatType, gatTime);
        if (0 != iReturnCode) {
            rglog.error("异步调用Dubbo服务提交失败,iReturnCode:<{}>", iReturnCode);
        }
        rglog.debug("异步调用Dubbo服务提交成功,iReturnCode:<{}>", iReturnCode);
        return iReturnCode;
    }

    private void dubboInnerCallBegin() {
        //mod by lizhangtao 20201201 单个线程内都是线程安全
        if (!StringUtil.isEmptyOrNull((String) ThreadVar.getVar(DUBBO_INNER_CALL_FLAG))) {
            AtomicInteger atomicInteger = (AtomicInteger) ThreadVar.getVar(DUBBO_INNER_CALL_NUM);
            atomicInteger.getAndIncrement();
        } else {
            ThreadVar.setVar(DUBBO_INNER_CALL_FLAG, "true");
            AtomicInteger atomicInteger = new AtomicInteger(0);
            ThreadVar.setVar(DUBBO_INNER_CALL_NUM, atomicInteger);
        }

    }

    private void dubboInnerCallEnd() {
        //mod by lizhangtao 20201201 单个线程内都是线程安全
        AtomicInteger atomicInteger = (AtomicInteger) ThreadVar.getVar(DUBBO_INNER_CALL_NUM);
        if (atomicInteger.get() == 0) {
            ThreadVar.removeVar(DUBBO_INNER_CALL_FLAG);
        } else {
            atomicInteger.getAndDecrement();
        }
    }

    private void resourceInit() {
        if (XmlTree.hasThreadTree()) {
            if (StringUtil.isEmptyOrNull((String) ThreadVar.getVar(DUBBO_INNER_CALL_FLAG))) {
                rglog.warn("Dubbo作为服务方存在未清理的内部树，先清理再处理业务!");
                resourceRelease();
                dubboServiceThreadInit(false);
                new XmlTree(AppParamConstant.XML_ROOT_NAME);
                rglog = RgLog.getLogger(this.getClass().getName());
                rglog.setLogPath(BulletinBoard.getMachine().getLogPath());
                rglog.setLogFormat(BulletinBoard.getMachine().getLogFormat());
                rglog.createLogKey();
            } else {
                rglog.debug("Dubbo服务应用内调用,不再重新初始化日志logKey!");
                dubboServiceThreadInit(true);
            }
        } else {
            dubboServiceThreadInit(false);
            new XmlTree(AppParamConstant.XML_ROOT_NAME);
            rglog = RgLog.getLogger(this.getClass().getName());
            rglog.setLogPath(BulletinBoard.getMachine().getLogPath());
            rglog.setLogFormat(BulletinBoard.getMachine().getLogFormat());
            rglog.createLogKey();
        }
    }

    private void resourceRelease() {
        if (StringUtil.isEmptyOrNull((String) ThreadVar.getVar(DUBBO_INNER_CALL_FLAG))) {
            /* 资源释放 */
            ThreadVar.clear();
            ThreadVar.clear(Thread.currentThread());
        }
    }
}
